package MathTextModeler
{
	import MathText.WordProblem;
	import MathText.WordProblemDisplay;
	
	import MyDisplayObjects.ClickableMCEvent;
	import MyDisplayObjects.DraggableMC;
	import MyDisplayObjects.MessageBox;
	import MyDisplayObjects.MyButton;
	import MyDisplayObjects.TextFeedbackDisplay;
	
	import TaskStructure.LogEvent;
	import TaskStructure.WordProblemItem;
	
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.utils.getQualifiedClassName;
	
	/** An object of this class provides a space to model sentences (DiagramDisplay) and a set of tools to create that model (SentenceTools),
	 *  and a button to press when completed.
	 */
	public class SentenceModeler extends MovieClip
	{
		// static constants
		public static const SENTENCE_DISPLAY_HEIGHT_PROP:Number = .8;
		// private vars, inaccessible outside of class
		private var optionsXML:XML, imagesContextXML:XML, errorsXML:XML;
		private var wordProblem:WordProblem;
		private var wordProblemDisplay:WordProblemDisplay;
		private var sentenceIndex:int;
		private var _diagramDisplay:DiagramDisplay;
		private var _toolDisplay:ToolDisplay;
		private var hidePeripheralText:Boolean, modelSentencesInSerial:Boolean;
		private var selectingDiagram:Boolean = false;
		private var sentenceCount:uint;
		private var trashTField:TextField;
		
		public function get diagramDisplay():DiagramDisplay{return this._diagramDisplay;}
		public function get toolDisplay():ToolDisplay{return this._toolDisplay;}
		public function get diagramContainers():Array{return _diagramDisplay.diagramContainers;}
		public function get currentFaceType():String{return toolDisplay.currentFaceType;}
		
		/** This constructor can be used to create a new sentence display or instantiate an old one */
		public function SentenceModeler(width:Number, height:Number, wordProblemDisplay:WordProblemDisplay, sentenceIndex:int, optionsXML:XML, imagesContextXML:XML, errorsXML:XML, initialToolDisplay:String="", odiagramContainers:Array = null)
		{
			this.graphics.drawRect(0, 0, width, height);
			this.wordProblemDisplay = wordProblemDisplay;
			this.wordProblem = wordProblemDisplay.wordProblem;
			this.sentenceCount = wordProblem.numSentencesToModel;
			this.sentenceIndex = sentenceIndex;
			this.optionsXML = optionsXML;
			this.imagesContextXML = imagesContextXML;
			this.errorsXML = errorsXML;
			if (optionsXML.child("sentence").(attribute("hidePeripheralText") == "true").length() > 0) { hidePeripheralText = true; } else { hidePeripheralText = false; }
			if (optionsXML.child("sentence").(attribute("modelSentencesInSerial")=="true").length() > 0){ modelSentencesInSerial = true; } else { modelSentencesInSerial = false; }
			
			// tools
			if (modelSentencesInSerial){
				_toolDisplay = new ToolDisplay (width*1/4, height, new Array("combine", "change", "compare"), optionsXML.child("tools")[0], imagesContextXML..tools[0], wordProblem, initialToolDisplay, "Continue", "Back");
				_toolDisplay.disableModeling = true;
			} else {
				_toolDisplay = new ToolDisplay (width*1/4, height, new Array("combine", "change", "compare"), optionsXML.child("tools")[0], imagesContextXML..tools[0], wordProblem, initialToolDisplay, "Continue", "Clear");
			}			
			this.addChild(_toolDisplay);
			
			// space for model
			if (odiagramContainers == null)
			{
				_diagramDisplay = new DiagramDisplay (width*3/4, height*SENTENCE_DISPLAY_HEIGHT_PROP, wordProblem, toolDisplay.currentFaceType, optionsXML.child("display")[0], imagesContextXML);
				// name sentence display
				if (sentenceIndex == 0){_diagramDisplay.name = "left";}
				else if (sentenceIndex == 1){_diagramDisplay.name = "middle";}
				else if (sentenceIndex == 2){_diagramDisplay.name = "right";}
			} else
			{
				_diagramDisplay = new DiagramDisplay (width*3/4, height*SENTENCE_DISPLAY_HEIGHT_PROP, wordProblem, initialToolDisplay, optionsXML.child("display")[0], imagesContextXML, odiagramContainers);
			}
			_diagramDisplay.addEventListener(Event.REMOVED, handleObjectRemovedFromDiagramDisplay);
			_diagramDisplay.x = toolDisplay.x + toolDisplay.width;
			
			trashTField = new TextField();
			trashTField.selectable = false;
			trashTField.mouseEnabled = false;
			trashTField.defaultTextFormat = new TextFormat("Arial", this.width/35, 0xFFFFFF, null, null, null, null, null, "center");
			trashTField.text = "Move objects here to remove them.";
			trashTField.multiline = true;
			trashTField.wordWrap = true;
			trashTField.background = true;
			trashTField.backgroundColor = 0xFFAAAA;
			trashTField.width = _diagramDisplay.width;
			trashTField.x = _diagramDisplay.x;
			trashTField.height = this.height - _diagramDisplay.y -_diagramDisplay.height;
			trashTField.y = _diagramDisplay.y + diagramDisplay.height;
			this.addChild(trashTField);
			
			_diagramDisplay.addEventListener(Event.INIT, handleDisplayLoaded);
			this.addChild(_diagramDisplay);
			
		}
		private function handleDisplayLoaded (event:Event):void{
			
			diagramDisplay.removeEventListener(Event.INIT, handleDisplayLoaded);
			if (modelSentencesInSerial){
				modelInSerial();
			} else {
				modelInParallel();
			}
		}
	
		public function restore():void{
			this.addChild(toolDisplay);
			toolDisplay.showModelingTools();	
			_diagramDisplay.addEventListener(Event.REMOVED, handleObjectRemovedFromDiagramDisplay);
			_diagramDisplay.x = toolDisplay.x + toolDisplay.width;
			this.addChild(_diagramDisplay);
			sentenceIndex--;
			diagramDisplay.switchModes();
			if (modelSentencesInSerial){
				modelInSerial();
			} else {
				modelInParallel();
			}
		}
		
		///////////////////////////////////////////////////// NAVIGATION //////////////////////////////////////////////////
		
		//////////////////////// PARALLEL //////////////////////////////
		
		private function modelInParallel ():void {
			this.stage.addChild(new MessageBox("Select a box to start putting pictures in.", 700, 120, MessageBox.EPHEMERAL_TYPE, "m", 400));
			diagramDisplay.selectAContainerStarted();
			diagramDisplay.addEventListener(Event.SELECT, handleDiagramContainerSelected_parallel);
			toolDisplay.addEventListener(Event.SELECT, handleTabSelected);
		}
		private function handleDiagramContainerSelected_parallel(event:Event):void
		{
			toolDisplay.addEventListener(ClickableMCEvent.CLICKED, handleToolClicked_parallel);
			toolDisplay.addEventListener(Event.COMPLETE, handleContinuePressed_parallel);
			toolDisplay.addEventListener(Event.CANCEL, handleBackPressed_parallel);
		}
		/** This function handles a click on one of the buttons on the tool face */
		private function handleToolClicked_parallel (event:ClickableMCEvent):void
		{
			event.stopPropagation();  // a bubbling event, stop propogation
			_diagramDisplay.addImageToDisplay(imagesContextXML..display.child(toolDisplay.currentFaceType).child(event.name)[0], event.name, true); 
		}
		/** When user clicks the continue button dispatch complete event */
		private function handleContinuePressed_parallel (event:Event):void
		{
			event.stopPropagation();
			if (checkFinalConditions())
			{
				toolDisplay.removeEventListener(ClickableMCEvent.CLICKED, handleToolClicked_parallel);
				toolDisplay.removeEventListener(Event.COMPLETE, handleContinuePressed_parallel);
				toolDisplay.removeEventListener(Event.CANCEL, handleBackPressed_parallel);
				toolDisplay.removeEventListener(Event.SELECT, handleTabSelected);
				diagramDisplay.removeEventListener(Event.REMOVED, handleObjectRemovedFromDiagramDisplay);
				this.dispatchEvent(new LogEvent(LogEvent.TRANSITION, "continue", diagramDisplay.name, 0, 0));
				this.dispatchEvent (new Event(Event.COMPLETE));
			}			
		}
		/** When user clicks the continue button dispatch complete event */
		private function handleBackPressed_parallel (event:Event):void
		{
			_diagramDisplay.removeAll();
		}
		
		///////////////////// SERIAL  //////////////////////////////////////
		private function modelInSerial():void
		{
			toolDisplay.addEventListener(Event.SELECT, handleTabSelected);
			modelNextSentence();	
		}
		
		/** This function initiates a new SentenceModeler, adds an event listener to it */
		private function modelNextSentence():void
		{
			var message:MessageBox;
			if (sentenceIndex < sentenceCount)
			{
				if (hidePeripheralText)
				{
					wordProblemDisplay.displaySubstring(wordProblemDisplay.wordProblem.sentences[sentenceIndex]);
				} else
				{
					wordProblemDisplay.highlightSubstring(wordProblemDisplay.wordProblem.sentences[sentenceIndex]);
				}
				message = new MessageBox("Select a box to model this sentence in.", 700, 120, MessageBox.EPHEMERAL_TYPE, "m", 400);
				if (this.stage != null) this.stage.addChild(message);
				diagramDisplay.selectAContainerStarted();
				diagramDisplay.addEventListener(Event.SELECT, handleDiagramContainerSelected_serial);
				toolDisplay.addEventListener(Event.CANCEL, handleBackPressed_serial_intermediate);
				
			} else
			{
				if (checkFinalConditions())
				{
					toolDisplay.removeEventListener(ClickableMCEvent.CLICKED, handleToolClicked_serial);
					toolDisplay.removeEventListener(Event.COMPLETE, handleContinuePressed_serial);
					toolDisplay.removeEventListener(Event.CANCEL, handleBackPressed_serial);
					toolDisplay.removeEventListener(Event.SELECT, handleTabSelected);
					diagramDisplay.removeEventListener(Event.REMOVED, handleObjectRemovedFromDiagramDisplay);
					this.dispatchEvent(new LogEvent(LogEvent.TRANSITION, "model_complete", diagramDisplay.name, 0, 0));
					this.dispatchEvent (new Event(Event.COMPLETE));
				} 
			}
		}
		/* Gives the user a chance to choose the display container for the previous sentence */
		private function handleBackPressed_serial_intermediate (event:Event):void{
			_diagramDisplay.removeEventListener(Event.SELECT, handleDiagramContainerSelected_serial);
			toolDisplay.removeEventListener(Event.CANCEL, handleBackPressed_serial_intermediate);
			_diagramDisplay.removeAllFromPreviouslySelectedDiagramContainer();
			_diagramDisplay.pushReferencesBack();
			_diagramDisplay.selectAContainerStopped();
			sentenceIndex--;
			toolDisplay.disableModeling = true;
			modelNextSentence();
		}
		private function handleDiagramContainerSelected_serial(event:Event):void
		{
			toolDisplay.removeEventListener(Event.CANCEL, handleBackPressed_serial_intermediate);
			toolDisplay.disableModeling = false;
			toolDisplay.addEventListener(ClickableMCEvent.CLICKED, handleToolClicked_serial);
			toolDisplay.addEventListener(Event.COMPLETE, handleContinuePressed_serial);
			toolDisplay.addEventListener(Event.CANCEL, handleBackPressed_serial);
		}
		/** This function handles a click on one of the buttons on the tool face */
		private function handleToolClicked_serial (event:ClickableMCEvent):void
		{
			event.stopPropagation();  // a bubbling event, stop propogation
			diagramDisplay.addImageToDisplay(imagesContextXML..display.child(toolDisplay.currentFaceType).child(event.name)[0], event.name, true); 
		}
		/** When user clicks the continue button dispatch complete event */
		private function handleContinuePressed_serial (event:Event):void
		{
			event.stopPropagation();
			sentenceIndex++;
			_diagramDisplay.pushReferencesForward();
			modelNextSentence();
		}
		/** When user clicks the continue button dispatch complete event */
		private function handleBackPressed_serial (event:Event):void
		{
			_diagramDisplay.removeAllFromSelectedDiagramContainer();
			modelNextSentence();
		}
		/** For both serial and parallel if tab is selected, switch up the display */
		private function handleTabSelected (event:Event):void{
			// get tab name
			var type:String = toolDisplay.currentFaceType;
			if (type.toLowerCase() == "combine"){
				diagramDisplay.displayCombine();
			} else if (type.toLowerCase() == "change"){
				diagramDisplay.displayChange();
			} else if (type.toLowerCase() == "compare"){
				diagramDisplay.displayCompare();
			} 		
		}
		
		/** Have we met final conditions (mainly, is the number model correct and does it match the problem) */
		private function checkFinalConditions ():Boolean
		{
			var node:XML;
			var message:MessageBox;
			if (errorsXML..sentenceModel..error.(attribute("id")=="NO_SUBJECT").length() > 0)
			{
				node = errorsXML..sentenceModel..error.(attribute("id")=="NO_SUBJECT")[0];
				// make sure it is enabled
				if (node.attribute("enabled") != "false" && node.attribute("halting") != "false")
				{
					if (_diagramDisplay.numOthers == 0)
					{
						this.stage.addChild(new MessageBox(node.toString(), 700, 120, MessageBox.EPHEMERAL_TYPE, "model_error", 400));
						return false;
					}
				}
			} 
			if (errorsXML..sentenceModel..error.(attribute("id")=="NO_QUANTITY").length() > 0)
			{
				node = errorsXML..sentenceModel..error.(attribute("id")=="NO_QUANTITY")[0];
				// make sure it is enabled
				if (node.attribute("enabled") != "false" && node.attribute("halting") != "false")
				{
					if (_diagramDisplay.numUnits + _diagramDisplay.numVariables == 0)
					{
						this.stage.addChild(new MessageBox(node.toString(), 700, 120, MessageBox.EPHEMERAL_TYPE, "model_error", 400));
						return false;
					}
				}
			} 
			if (errorsXML..sentenceModel..error.(attribute("id")=="NONMATCHING_NUMBER").length() > 0)
			{
				node = errorsXML..sentenceModel..error.(attribute("id")=="NONMATCHING_NUMBER")[0];
				// make sure it is enabled
				if (node.attribute("enabled") != "false" && node.attribute("halting") != "false")
				{
					
					if (String(wordProblem.sentences[sentenceIndex]).search(String(_diagramDisplay.numUnits)) == -1 && String(wordProblem.sentences[sentenceIndex]).search(RegExp(/[0-9]+/)) != -1)
					{
						this.stage.addChild(new MessageBox(node.toString(), 700, 120, MessageBox.EPHEMERAL_TYPE, "model_error", 400));
						return false;
					}
				}
			} 
			if (errorsXML..sentenceModel..error.(attribute("id")=="COMPARE_WRONG_SIDE").length() > 0 && diagramDisplay.hasCompare)
			{
				node = errorsXML..sentenceModel..error.(attribute("id")=="COMPARE_WRONG_SIDE")[0];
				if (node.attribute("enabled") != "false" && node.attribute("halting") != "false")
				{
					// WHO HAS MORE?
					var i:uint; var unitMC:DraggableMC;
					if (wordProblem.subject1.getQuantity() > wordProblem.subject2.getQuantity())
					{ // subject1 has more, should be more object on right side
						for (i; i < diagramDisplay.numUnits; i++)
						{
							unitMC = diagramDisplay.getUnitAt(i);
							if (unitMC.x < (diagramDisplay.width/2 - unitMC.width)) 
							{
								this.stage.addChild(new MessageBox(node.toString(), 700, 120, MessageBox.EPHEMERAL_TYPE, "model_error", 400));
								return false;
							}
						}
					} else if (wordProblem.subject1.getQuantity() < wordProblem.subject2.getQuantity())
					{ // subject1 has more, should be more object on left side
						for (i; i < diagramDisplay.numUnits; i++)
						{
							unitMC = diagramDisplay.getUnitAt(i);
							if (unitMC.x > diagramDisplay.width/2) 
							{
								this.stage.addChild(new MessageBox(node.toString(), 700, 120, MessageBox.EPHEMERAL_TYPE, "model_error", 400));
								return false;
							}
						}
					} 
				}
			}
			return true;
		}
		
		//////////////////////////// INTERACTION BETWEEN TOOL AND MODEL DISPLAY ////////////////////////////////////
		/** This function handles a click on one of the buttons on the tool face */
		private function handleToolClicked (event:ClickableMCEvent):void
		{
			event.stopPropagation();  // a bubbling event, stop propogation
			_diagramDisplay.addImageToDisplay(imagesContextXML..display.child(toolDisplay.currentFaceType).child(event.name)[0], event.name, true); 
		}
		
		private function handleObjectRemovedFromDiagramDisplay (event:Event):void
		{
			// for objects being removed from display, not for removing the display itself
			if (flash.utils.getQualifiedClassName(event.target) == "MyDisplayObjects::DraggableMC" || flash.utils.getQualifiedClassName(event.target) == "MyDisplayObjects::DraggableImage" || flash.utils.getQualifiedClassName(event.target) == "MyDisplayObjects::DraggableShape")
			{
				toolDisplay.enableButton(DraggableMC(event.target).name);
			} 
		}
		
	}
}